/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#include "MaCI_EmergencyStopMainFrame.h"


DEFINE_EVENT_TYPE(MY_EVENT_EMERGENCYSTOPEVENT);
DEFINE_EVENT_TYPE(MY_EVENT_EMERGENCYSTOPDATAEVENT);
DEFINE_EVENT_TYPE(MY_EVENT_CONNECTSERVICES);
DEFINE_EVENT_TYPE(MY_EVENT_ACTIVATE_EMERGENCYSTOP);
DEFINE_EVENT_TYPE(MY_EVENT_UPDATE_INFOTEXT);
//cretae event table for catching custom event
BEGIN_EVENT_TABLE(MaCI_EmergencyStopMainFrame, wxFrame)
 EVT_COMMAND(wxID_ANY, MY_EVENT_EMERGENCYSTOPEVENT, MaCI_EmergencyStopMainFrame::OnEmergencyStopQuestionResponse)
 EVT_COMMAND(wxID_ANY, MY_EVENT_EMERGENCYSTOPDATAEVENT, MaCI_EmergencyStopMainFrame::OnNewEmergencyStopDataEvent)
 EVT_COMMAND(wxID_ANY, MY_EVENT_CONNECTSERVICES, MaCI_EmergencyStopMainFrame::OnConnectService)
 EVT_COMMAND(wxID_ANY, MY_EVENT_ACTIVATE_EMERGENCYSTOP, MaCI_EmergencyStopMainFrame::OnEmergencyStopClicked)
 EVT_COMMAND(wxID_ANY, MY_EVENT_UPDATE_INFOTEXT, MaCI_EmergencyStopMainFrame::OnUpdateInfoText)
END_EVENT_TABLE();


MaCI_EmergencyStopMainFrame::MaCI_EmergencyStopMainFrame( wxWindow* parent )
:
  CSync(1,1),
  CThread(2),
  MainFrame( parent ),
  iTimeoutTime(5000),
  iServiceList(),
  iMaCICtrlClient(),
  iConnected(false),
  iGIMIPtr(NULL),
  iEmergencyStopClient(NULL),
  iVersion("3.0"),
  iExecutorKeyboardWatcher(),
  iExecutorUSBPanicButton(),
  keyWatcherExecuted(false),
  iUSBPanicButtonExecuted(false),
  iUSBThreadRunning(false),
  iReadPipeIntToKeyboard(-1),
  iWritePipeIntToKeyboard(-1),
  iReadPipeIntToUSBPanicButton(-1),
  iWritePipeIntToUSBPanicButton(-1),
  iKeyBindingInformationSet(false),
  iBindingInfo(NULL),
  iTestBindingInfo(NULL),
  iThreadRunning(false),
  iCanBindKey(true)
  

{


}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnAbout( wxCommandEvent& event )
{

  MaCI_EmergencyStopAbout_Dialog dia(this,iVersion);
  dia.ShowModal();
}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnHowTo( wxCommandEvent& event )
{

  MaCI_EmergencyStopHelp_Dialog dia(this);
  dia.ShowModal();

}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnEmergencyStopClicked( wxCommandEvent& event )
{
  //

  if(iEmergencyStopClient){
    int times = 0;
    if(iEmergencyStopClient->ActivateEmergencyStop(iTimeoutTime)){
      dPrint(1,"Succesfully activated emergency stop!");
   
    }else{
      dPrint(1,"FAILED TO ACTIVATE EMERGENCY STOP!");
    }
  }else{
    dPrint(1,"Not connected, unable to call emergency stop");
  }
   

}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnDeactivate( wxCommandEvent& event )
{
  if(iEmergencyStopClient){
    MaCI_EmergencyStopDeactivationDialog *dia = new MaCI_EmergencyStopDeactivationDialog(this,
                                                                                         "Are you really sure to deactivate emergency stop from:",
                                                                                         iMaCISL,
                                                                                         KQuestionDeactivateEmergencyStop);
  
    dia->ShowModal();
  }
    
}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnKeyBind( wxCommandEvent& event )
{
  //Start keywatcher and thread
  if(!keyWatcherExecuted && iCanBindKey){


    //Create pipe
    TStringArray params;
    int pipefd[2];
    int pipefd2[2];
    pipe(pipefd);
    pipe(pipefd2);
    iWritePipeIntToKeyboard = pipefd[1];
    iReadPipeIntToKeyboard = pipefd2[0];
    std::ostringstream stm;
    std::ostringstream stm2;


    stm << pipefd[0];
    std::string s = stm.str();
    params.push_back(s);
    stm2 << pipefd2[1];
    s = stm2.str();
    params.push_back(s);
    iExecutorKeyboardWatcher.Execute("KeyWatcherMain",
                      params);

    ///Wait for response 
    char buf;
    if(read(iReadPipeIntToKeyboard,&buf,1) >0){
      if(atoi((const char *)&buf) == KMessageFailedToOpenKeyWatcher){
        //Failed to open key watcher.
        dPrint(1,"Failed to open key watcher..");
        
        iCanBindKey = false;
        iExecutorKeyboardWatcher.Terminate();
      }else if(atoi((const char *)&buf) ==  KMessageSuccesfullyOpenedKeyWatcher){
        //Succesfully openened keywatcher. Start pipe listening thread.
        dPrint(1,"Opened keywatcher..");
        keyWatcherExecuted = true;
        iThreadRunning = true;
        CThread::RunThread(0);
       
      }
    }
  }

  if(!iCanBindKey){
    MaCI_EmergencyStopInformation_Dialog *info = new MaCI_EmergencyStopInformation_Dialog(this,
                                                                                          "Failed to bind key. Have you compiled Emergency stop component with sudo rights?");
        

    info->ShowModal();
   

  }

  //Then ask for keybinding
  if(!iKeyBindingInformationSet && iCanBindKey){
    std::ostringstream stm3;
    stm3 << KCommandBindKey;

    write(iWritePipeIntToKeyboard,stm3.str().c_str(),1);
    iBindingInfo = new MaCI_EmergencyStopInformation_Dialog(this,"Please press a key to bind it");
    iBindingInfo->ShowModal();
    char buf;  
    
  }
   
  

}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnTestButtonClicked( wxCommandEvent& event )
{
  if(iCanBindKey && keyWatcherExecuted){

    std::ostringstream stm;
    stm << KCommandTestKey;

    write(iWritePipeIntToKeyboard,stm.str().c_str(),1);
    iTestBindingInfo = new MaCI_EmergencyStopInformation_Dialog(this,"Please press binded key ");
    iTestBindingInfo->ShowModal();

   

  }else{
    MaCI_EmergencyStopInformation_Dialog *info = new MaCI_EmergencyStopInformation_Dialog(this,
                                                                                          "No key binded.");
        

    info->ShowModal();

  }

}
//*****************************************************************************
void  MaCI_EmergencyStopMainFrame::OnEmergencyStopQuestionResponse(wxCommandEvent& event)
{
  if(event.GetInt() == KQuestionDeactivateEmergencyStop){
    if(iEmergencyStopClient->DeactivateEmergencyStop(iTimeoutTime)){
      dPrint(1,"Succesfully deactivated emergency stop");
      
    }else{
      dPrint(1,"Failed to deactivate emergency stop");
    }
  }else if(event.GetInt() == KQuestionDisconnectFromServer){
    if(iThreadRunning){
      iThreadRunning = false;
    }
    if(iUSBThreadRunning){
      iUSBThreadRunning = false;
    }
    if(keyWatcherExecuted){

      iExecutorKeyboardWatcher.Terminate();
    }
    if(iUSBPanicButtonExecuted){
      std::ostringstream stm;
      stm << KMessageCloseUSBPanicButton;
      write(iWritePipeIntToUSBPanicButton,stm.str().c_str(),1);
      ownSleep_ms(1000);
      iExecutorUSBPanicButton.Terminate();
    }

    DisconnectService();


    Destroy();

  }
}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnServiceAssignment(const Gimbo::TServiceAssignmentList &aServiceList)
{

  dPrint(ODSTATUS,"Event: OnServiceAssignment");
  if(!iConnected ){
    iServiceList = aServiceList;
    wxCommandEvent e( MY_EVENT_CONNECTSERVICES,GetId());
    AddPendingEvent(e);

  }else{

   
  }
  
;
 
   
}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnEmergencyStopDataEvent(const MaCI::EmergencyStop::CEmergencyStopData &aData, 
                              const unsigned int aDataSequence,
                              MaCI::EmergencyStop::CEmergencyStopClient &aEmergencyStopClient)
{
  if(&aEmergencyStopClient == iEmergencyStopClient){
    CSync::Lock();
    iEmergencyStopData = aData;
    //Add event
    wxCommandEvent e(MY_EVENT_EMERGENCYSTOPDATAEVENT, GetId());
    AddPendingEvent(e);
    CSync::Unlock();
  }



}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnNewEmergencyStopDataEvent(wxCommandEvent &aEvent)
{
  
  const MaCI::EmergencyStop::TStatus *status = iEmergencyStopData.GetStatus();
  
  if(status->emergencyStopStatus == MaCI::EmergencyStop::KStatusEmergencyStopDeactivated){
    m_bpButton_EmergencyStop->SetBitmapLabel(wxBitmap( wxT("Stop.bmp")));
    m_bpButton_EmergencyStop->SetBackgroundColour( wxColour( 255, 255, 0 ) );
      dPrint(1,"New status event deact");
  }else{
    m_bpButton_EmergencyStop->SetBitmapLabel(wxBitmap( wxT("Stopped.bmp")));
    m_bpButton_EmergencyStop->SetBackgroundColour( wxColour( 255, 0, 0 ) );
    dPrint(1,"New status event act");

  };

}
//*****************************************************************************

bool MaCI_EmergencyStopMainFrame::Initialize(gimi::GIMI *aGimiPtr, int aGimboID, int aReportID)
{

  bool result = true;
  
  // Store for future reference.
  iGIMIPtr = aGimiPtr;
 

  iMaCICtrlClient =new MaCI::MaCICtrl::CMaCICtrlClient(aGimiPtr);
  
  if (iMaCICtrlClient->Open()!=MaCI::KMaCIOK){
    
    dPrint(1,"Couldn't open MaCICtrl client");
    result = false;
  }
  dPrint(1,"Creating emergencystop client");
  iEmergencyStopClient = new MaCI::EmergencyStop::CEmergencyStopClient(aGimiPtr);
  iEmergencyStopClient->SetEmergencyStopClientCallback(this);
  // Start the Gimbo core.
  CComponent::Start(aGimiPtr, aGimboID, aReportID);

  CheckForUSBPanicButtonAndStartIt();


  return result;

}
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::CheckForUSBPanicButtonAndStartIt(){
 //Create pipe
  TStringArray params;
  int pipefd[2];
  int pipefd2[2];
  pipe(pipefd);
  pipe(pipefd2);
  iWritePipeIntToUSBPanicButton = pipefd[1];
  iReadPipeIntToUSBPanicButton = pipefd2[0];
  std::ostringstream stm;
  std::ostringstream stm2;
  
  
  stm << pipefd[0];
  std::string s = stm.str();
  params.push_back(s);
  stm2 << pipefd2[1];
  s = stm2.str();
  params.push_back(s);
  iExecutorUSBPanicButton.Execute("USBPanicButton",
                    params);

  ///Wait for response 
  char buf;

  if(read(iReadPipeIntToUSBPanicButton,&buf,1) >0){

    if(atoi((const char *)&buf) == KMessageFailedToOpenUSBPanicButton){
      //Failed to open USB Panic Button.
      //      iExecutor.Terminate();
      MaCI_EmergencyStopInformation_Dialog *info = new MaCI_EmergencyStopInformation_Dialog(this,
                                                                                            "Failed to find/open USB 'Panic button'. If USB button is connected it might be bound by usbhid. Check from help->howto how to unbind it. \n\n You can still bind a keyboard button for emergency stop or activate it by mouse.");
      

      info->ShowModal();
      iExecutorUSBPanicButton.Terminate();
    }else if(atoi((const char *)&buf) ==  KMessageSuccesfullyOpenedUSBPanicButton){
      //Succesfully openened keywatcher. Start pipe listening thread.

      iUSBPanicButtonExecuted = true;
      iUSBThreadRunning = true;
      CThread::RunThread(1);
      MaCI_EmergencyStopInformation_Dialog *info = new MaCI_EmergencyStopInformation_Dialog(this,
                                                                                            "Found USB 'Panic button'. It can be used for activating Emergency stop.");
      

      info->ShowModal();
      
    }
  }
}                                  
//*****************************************************************************

void MaCI_EmergencyStopMainFrame::OnConnectService(wxCommandEvent &event){
  bool result = false;
  MaCI::EMaCIError e;
  Gimbo::TServiceAssignmentList::const_iterator si;

  for (si=iServiceList.begin(); si!=iServiceList.end(); si++){

    if(si->majorType == GIMI_PROTOCOL_MACI_EMERGENCYSTOP){

      result =iEmergencyStopClient->SetDefaultTarget(MaCI::MaCICtrl::SMaCISL(si->MaCISL),iTimeoutTime);
      if(result){

         if ( (e = iEmergencyStopClient->Open()) != MaCI::KMaCIOK) {
           dPrint(1,"Failed to Open EmergencyStopClient instance! (%s)",
	     GetErrorStr(e).c_str());
           result = false;
         } else {
           if(iEmergencyStopClient->ActivateClient(iTimeoutTime)){

             iMaCISL = si-> MaCISL;
             wxString str= "Connected to: ";
             
             wxString target = si->MaCISL;
             m_staticText_Connection->SetLabel(str);
             m_staticText_target->SetLabel(target);
             result = true;
             iConnected = true;
             break;
           }else{
             dPrint(1,"ClientActivation failed!");
           }
         }
        
      }

    }
  }
}
  
//*****************************************************************************

bool MaCI_EmergencyStopMainFrame::DisconnectService(){
  if(iEmergencyStopClient->DeactivateClient(iTimeoutTime)){
    iEmergencyStopClient->Close();
    delete iEmergencyStopClient;

  }else{
    dPrint(1,"Failed to deactivate");

  }
}
//*****************************************************************************

/*void MaCI_EmergencyStopMainFrame::OnKeyPressed()
{
  
  wxCommandEvent e(MY_EVENT_ACTIVATE_EMERGENCYSTOP, GetId());
  AddPendingEvent(e);
}*/
//*****************************************************************************
void MaCI_EmergencyStopMainFrame::OnClose( wxCloseEvent& event )
{
  
  DoClosing();

}
//*****************************************************************************
void MaCI_EmergencyStopMainFrame::OnQuit( wxCommandEvent& event ){

  DoClosing();
}
//*****************************************************************************
void MaCI_EmergencyStopMainFrame::DoClosing(void){
  if(iEmergencyStopClient){
    MaCI_EmergencyStopDeactivationDialog *dia = new MaCI_EmergencyStopDeactivationDialog(this,
                                                                                         "Are you really sure to quit and disconnect from:",
                                                                                         iMaCISL,
                                                                                         KQuestionDisconnectFromServer);
    
    dia->ShowModal();
  }
}
//*****************************************************************************

int MaCI_EmergencyStopMainFrame::ThreadFunction(const int aThreadNumber)
{
  char buf;
  dPrint(1,"Thread function called (%d)",aThreadNumber);
  //Keywatcher thread!
  if(aThreadNumber == 0){
    while(iThreadRunning){
      
      struct pollfd pfds[1] = {{ iReadPipeIntToKeyboard, POLLIN, 0 }};
      int result = poll(pfds, 1, 1000);
      if (result < 0) {
        dPrintLE(3,"poll() failed for 'read' file descriptor (%d)",
                 iReadPipeIntToKeyboard);
        
      }else if(result > 0){
        
        if(read(iReadPipeIntToKeyboard,&buf,1) >0){
          if(atoi((const char *)&buf) == KMessageKeyBinded){
            
            iKeyBindingInformationSet = false;
            iBindingInfo->Destroy();
            
          }else if(atoi((const char *)&buf) == KMessageEmergencyKeyPressed){
            
            if(iEmergencyStopClient->ActivateEmergencyStop(iTimeoutTime)){
              dPrint(1,"Succesfully activated emergency stop!");
              
            }else{
              dPrint(1,"FAILED TO ACTIVATE EMERGENCY STOP!");
            }
            
            
          }else if(atoi((const char *)&buf) == KMessageTestKeyPressed){
            wxCommandEvent e(MY_EVENT_UPDATE_INFOTEXT,GetId());
            e.SetString(wxString("Binded key Pressed! "));
            AddPendingEvent(e);

            
          }else if(atoi((const char *)&buf) == KMessageAnotherKeyPressedWhenTesting){
            wxCommandEvent e(MY_EVENT_UPDATE_INFOTEXT,GetId());
            e.SetString(wxString("Another key pressed than binded key "));
            AddPendingEvent(e);
            
          }else{
            dPrint(1,"didn't recognize message %d",atoi((const char *)&buf));
          }
          
        }
      }
    
    
    }
  }else if(aThreadNumber == 1){
    //USB PANIC BUTTON THREAD

    while(iUSBThreadRunning){

      struct pollfd pfds[1] = {{ iReadPipeIntToUSBPanicButton, POLLIN, 0 }};
      int result = poll(pfds, 1, 1000);
      
      if (result < 0) {
        dPrintLE(3,"poll() failed for 'read' file descriptor (%d)",
                 iReadPipeIntToUSBPanicButton);
        
      }else if(result > 0){
        if(read(iReadPipeIntToUSBPanicButton,&buf,1) >0){
          dPrint(1,"GOT MESSAGE");
          if(atoi((const char *)&buf) == KMessageUSBEmergencyKeyPressed){
            if(iEmergencyStopClient->ActivateEmergencyStop(iTimeoutTime)){
              dPrint(1,"Succesfully activated emergency stop!");
              
            }else{
              dPrint(1,"FAILED TO ACTIVATE EMERGENCY STOP!");
            }
          }
            
        }else{
            dPrint(1,"didn't recognize message %d",atoi((const char *)&buf));
        }
      }
      


    }

  }
  
    
  
  return 0;
}
//*****************************************************************************
void MaCI_EmergencyStopMainFrame::OnUpdateInfoText(wxCommandEvent &aEvent)
{
  iTestBindingInfo->SetNewText(aEvent.GetString());

}
//*****************************************************************************
//*****************************************************************************
//***************************************************************************** 
